using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualBasic;

namespace YT.Controls
{
    public partial class DateTimeEx : UserControl
    {


        DateTime m_def_MinDate = new System.DateTime(1900, 1, 2, 0, 0, 0, 0);
        DateTime m_def_MaxDate = new System.DateTime(9998, 12, 31, 0, 0, 0, 0);
        BorderStyle m_borderSyle = BorderStyle.Fixed3D;

        string mstrValue = "    -  -  ";
        UserStyle mLsLock = UserStyle.CanWriteSelect;

        DateTime m_MinDate;
        DateTime m_MaxDate;
        DateTime m_LastDate = new System.DateTime(1900, 1, 1);

        DateTime mIniDate;
        bool m_bDateNull = false ;
        bool m_bIsColsed = false;

        public enum UserStyle
        {
            CanWriteSelect = 0,
            CanSelect,
            CanNotAll
        }

        public DateTimeEx()
        {
            InitializeComponent();

            this.LockStyle = UserStyle.CanWriteSelect;
            dtpDate.Value = DateTime.Now;
            this.txtDate.Text = "    -  -  "; 
            mIniDate = DateTime.Now; 
            m_bDateNull = true ;
        }


        public delegate void ValueChangedEventHandler(object sender, EventArgs e);
        public event ValueChangedEventHandler ValueChanged;
        protected virtual void OnValueChanged(EventArgs e)
        {
            if (ValueChanged != null)
            {
                ValueChanged(this, e);
            }
        }

        public new BorderStyle BorderStyle
        {
            get
            {
                return m_borderSyle;
            }
            set
            {
                m_borderSyle = value;
                if(value==BorderStyle.FixedSingle)
                    base.BorderStyle = value;
                else
                    base.BorderStyle = BorderStyle.None;
                this.txtDate.BorderStyle = value;
                SetSize();
            }
        }

        public override Color ForeColor
        {
            get
            {
                return this.txtDate.ForeColor;
            }
            set
            {
                this.txtDate.ForeColor = value;
            }
        }

        public DateTime MinDate
        {
            get
            {
                return m_MinDate;
            }
            set
            {
                if (value.Year < 1900)
                    m_MinDate = m_def_MinDate;
                else
                    m_MinDate = value;
            }

        }

        public override string Text
        {
            get
            {
                    return mstrValue;
            }
            set
            {
                if (value != "" && value !="    -  -  ")
                    mstrValue = ValidDate(value).ToString("yyyy-MM-dd");
                else
                    mstrValue = "    -  -  ";
                txtDate.Text = mstrValue;
            }
        }

        public string Value
        {
            get
            {
                     return mstrValue;
            }
            set
            {
                if (value == null || value.ToString().Trim() == "" || value.ToString().Trim() == "-  -")
                {
                    mstrValue = "    -  -  ";
                    m_bDateNull = true;
                }
                else
                {
                    dtpDate.Value = Convert.ToDateTime(value);
                    mstrValue = dtpDate.Value.ToString("yyyy-MM-dd");
                    m_bDateNull = false;
                }
                txtDate.Text = mstrValue;
            }
        }

        public DateTime MaxDate
        {
            get
            {
                return m_MaxDate;
            }
            set
            {
                if (value.Year > 9998)
                    m_MaxDate = m_def_MaxDate;
                else
                    m_MaxDate = value;
            }
        }

        public UserStyle LockStyle
        {
            get
            {
                return mLsLock;
            }
            set
            {
                mLsLock = value;
                if (mLsLock == UserStyle.CanNotAll)
                {
                    this.dtpDate.Visible = false;
              //      this.Width = 67;
              //      this.txtDate.Width = 67;
                    this.txtDate.ReadOnly = true;
                    this.txtDate.BackColor = System.Drawing.SystemColors.Window;
                }
                else if (mLsLock == UserStyle.CanSelect)
                {
                    this.dtpDate.Visible = true;
                //    this.Width = 87;
                    this.txtDate.ReadOnly = true;
                    this.txtDate.BackColor = System.Drawing.SystemColors.Window;
                }
                else
                {
                    this.dtpDate.Visible = true;
                 //   this.Width = 87;
                    this.txtDate.ReadOnly = false;
                }
            }
        }

        #region Private Event

        #region for DateTimeEx
        private void DateTimeEx_Load(object sender, System.EventArgs e)
        {
            this.dtpDate.SendToBack();

            m_MinDate = m_def_MinDate;
            m_MaxDate = m_def_MaxDate;
            SetSize();

        }

        private void DateTimeEx_EnabledChanged(object sender, System.EventArgs e)
        {
            //if (!this.Enabled)
            //{
            //    //this.dtpDate.Visible = false;
            //    this.Width = 67;
            //    this.txtDate.Width = 67;
                
            //}
            //else
            //{
            //    this.dtpDate.Visible = true;
            //    this.Width = 87;
            //}
        }
        #endregion

        #region for txtDate
        private void txtDate_TextChanged(object sender, System.EventArgs e)
        {
            if (txtDate.Text.Trim().Length < 1 || txtDate.Text.Trim() == "-  -")
            {
                m_bDateNull = true;
                this.Value = this.txtDate.Text; 
            }
            else
            {
                string strValidDate = "";
                strValidDate = ValidDate(txtDate.Text).ToString("yyyy-MM-dd");
                if (strValidDate == txtDate.Text)
                {
                    this.dtpDate.Value = ValidDate(txtDate.Text);
                    this.Value = this.txtDate.Text; 
                }
                
            }
            this.OnValueChanged(e);
            this.OnTextChanged(e);
            if (txtDate.Text.Trim() == "" || txtDate.Text.Trim() == "-  -") m_LastDate = Convert.ToDateTime("1900/01/01");   
        }

        private void txtDate_Click(object sender, System.EventArgs e)
        {
            if (!m_bDateNull)
            {
                SelNext(0);
            }
        }


        private void DateTimeEx_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            MessageBox.Show("The method or operation is not implemented.");
        }

        private void txtDate_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            if (mLsLock != UserStyle.CanWriteSelect) return;
            if (e.KeyCode == Keys.Delete && mLsLock == UserStyle.CanWriteSelect)
            {
                if (txtDate.SelectionLength == txtDate.Text.Length)
                {
                    txtDate.Text = "    -  -  ";
                }
                e.SuppressKeyPress = true;
                return;
            }

            if (e.KeyCode == Keys.Back)
            {
                e.SuppressKeyPress = true;
                return;
            }

        }

        public void txtDate_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
        {
            int intMidKeyAscii;
            intMidKeyAscii = e.KeyChar;

            if (mLsLock != UserStyle.CanWriteSelect) return;
            if (intMidKeyAscii > 47 && intMidKeyAscii < 58)
            {
                if (txtDate.SelectionStart >= 10)
                    return;
                else
                {
                    int intMidPos;
                    string strMid;
                    string strRight;
                    if (m_bDateNull)
                    {

                        txtDate.Text = Convert.ToChar(intMidKeyAscii) + DateTime.Today.ToString("yyyy-MM-dd").Substring(1);
                        m_bDateNull = false;
                        txtDate.SelectionStart = 1;
                    }
                    else
                    {
                        if (!IsNumeric(txtDate.Text.Substring(txtDate.SelectionStart, 1)))
                            intMidPos = txtDate.SelectionStart + 1;
                        else
                            intMidPos = txtDate.SelectionStart;

                        strMid = txtDate.Text.Substring(0, intMidPos);
                        strRight = txtDate.Text.Substring(intMidPos + 1);
                        txtDate.Text = strMid + Convert.ToChar(intMidKeyAscii).ToString() + strRight;
                        txtDate.SelectionStart = intMidPos + 1;
                        txtDate.SelectionLength = 0;
                    }

                }
            }
            e.KeyChar = Convert.ToChar(Keys.None);
        }


        private void txtDate_LostFocus(object sender, System.EventArgs e)
        {
            DateTime correctDate;

            if (!m_bDateNull)
            {
                correctDate = ValidDate(txtDate.Text);
                txtDate.Text = correctDate.ToString("yyyy-MM-dd");
            }
        }

        private void txtDate_Validating(System.Object sender, System.ComponentModel.CancelEventArgs e)
        {
            bool Cancel = e.Cancel;
            if (!m_bDateNull)
            {
                DateTime dtDate;
                dtDate = ValidDate(txtDate.Text);
                txtDate.Text = dtDate.ToString("yyyy-MM-dd");
            }
            e.Cancel = Cancel;
        }

        private void txtDate_Enter(object sender, EventArgs e)
        {
            txtDate.SelectionStart = 0;
        }

        #endregion

        #region for dtpDate
        private void dtpDate_CloseUp(object sender, System.EventArgs e)
        {
            System.DateTime dtDate = this.dtpDate.Value;
            string strDate = dtDate.ToString("yyyy-MM-dd");
            this.txtDate.Text = strDate;

            m_bIsColsed = true;
        }
        private void dtpDate_DropDown(object sender, EventArgs e)
        {
            if (this.txtDate.Text != "    -  -  ")
            {
                m_bIsColsed = false; // is open
                m_LastDate = Convert.ToDateTime(this.txtDate.Text);
            }
        }
        #endregion


        #endregion

        #region Private Function
        private DateTime ValidDate(String strDate)
        {
            int intYear;
            int intMonth;
            int intDay;
            DateTime dtValidDate;

            //            m_bChange = false;

            try
            {
                intYear = GetDatePart(strDate, 0);
                intMonth = GetDatePart(strDate, 1);
                intDay = GetDatePart(strDate, 2);

                if (intYear > m_MaxDate.Year) intYear = m_MaxDate.Year;
                if (intYear < m_MinDate.Year) intYear = m_MinDate.Year;
                if (intYear > m_MaxDate.Year || intYear < m_MinDate.Year)
                {
                    intYear = mIniDate.Year;
                }

                if (intYear == m_MaxDate.Year)
                {
                    if (intMonth > m_MaxDate.Month)
                    {
                        //                        m_bChange = true;
                        intMonth = mIniDate.Month;
                    }
                    else
                    {
                        if ((intMonth == m_MaxDate.Month) && (intDay > m_MaxDate.Day))
                        {
                            //                            m_bChange = true;
                            intDay = mIniDate.Day;
                        }
                    }
                }

                if (intYear == m_MinDate.Year)
                {
                    if (intMonth < m_MinDate.Month)
                    {
                        //                        m_bChange = true;
                        intMonth = mIniDate.Month;
                    }
                    else
                    {
                        if (intMonth == m_MinDate.Month && intDay < m_MinDate.Day)
                        {
                            //                            m_bChange = true;
                            intMonth = mIniDate.Month;
                        }
                    }
                }

                if (intMonth > 12)
                {
                    //                    m_bChange = true;
                    intMonth = mIniDate.Month;
                }

                if (intDay > 31)
                {
                    //                    m_bChange = true;
                    intDay = mIniDate.Day;
                }

                if (intYear - (intYear / 4) * 4 != 0)
                {
                    if (intMonth == 2 && intDay > 28)
                    {
                        intMonth = mIniDate.Month;
                    }
                }
                else
                {
                    if (intMonth == 2 && intDay > 29)
                    {
                        //                        m_bChange = true;
                        intMonth = mIniDate.Month;
                    }
                }

                if (intMonth == 4 || intMonth == 6 || intMonth == 9 || intMonth == 11)
                {
                    if (intDay > 30)
                    {
                        //                        m_bChange = true;
                        intDay = mIniDate.Day;
                    }
                }
                //                dtValidDate = DateSerial(intYear, intMonth, intDay);
                string strValidDate = intYear.ToString() + "-" + intMonth.ToString() + "-" + intDay.ToString();
                dtValidDate = Convert.ToDateTime(strValidDate);
                mIniDate = dtValidDate;
            }
            catch
            {
                dtValidDate = DateTime.Today;
                //                m_bChange = true;
            }

            return dtValidDate;
        }

        private int GetDatePart(string strDate, byte intType)
        {
            //********************************************************************************************
            //* Function: Get some part of inputed date string  according to current format string
            //* Input:    strDate--date string. e.g.01/01/2001
            //*           intType--0 output a integer in year part, 1 output a integer in month
            //*                    2 output a integer in day part
            //* Output:   a integer value
            //* Special:  can't correctly handle such case that no delimeter and only one "M" or "D"
            //*           character in m_customformat AND month>9 or date>9 in strDate
            //* Usage:    by using this functio,I can know which part in strDate is invalid and restore
            //*           corrpesponding part to its old value.
            //*           such if the user change current date "2001-4-22" in textbox to "2001-99-22",the
            //*           ocx will change the wrong date to "2001-4-22" by ValidDate function.
            //* e.g       if strDate="2001-4-22" m_customformat="yyyy/m/d" intType=0 then GetDatePart=2001
            //*           if strDate="2001-99-22" m_customformat="yyyy-MM-dd" intType=1 then GetDatePart=99
            //*********************************************************************************************
            string strMid;
            //            string strFormat;
            byte intYearbits;
            byte intMDbits;
            bool bWrong;
            int iGetDatePart = 0;

            bWrong = false;
            if (strDate.Length < 4 || strDate.Length > 10)
            {
                bWrong = true;
            }
            intYearbits = 4;

            intMDbits = 2;

            intMDbits = 2;


            switch (intType)
            {
                case 0:                     //Get year
                    strMid = strDate.Substring(0, intYearbits);

                    if (IsNumeric(strMid) && !bWrong)
                    {
                        iGetDatePart = ToInt32(strMid, 0);
                    }
                    else
                    {
                        iGetDatePart = DateTime.Today.Year;
                    }
                    break;
                case 1:                     //Get month
                    strMid = strDate.Substring(5, intMDbits);
                    if (IsNumeric(strMid) && !bWrong)
                        iGetDatePart = ToInt32(strMid, 0);
                    else
                        iGetDatePart = DateTime.Today.Month;
                    break;
                case 2:                     //Get day
                    strMid = strDate.Substring(8, intMDbits);
                    if (IsNumeric(strMid) && !bWrong)
                        iGetDatePart = ToInt32(strMid, 0);
                    else
                        iGetDatePart = DateTime.Today.Day;
                    break;
                default:
                    iGetDatePart = 0;
                    break;
            }

            return iGetDatePart;
        }

        private string GetNullValue()
        {
            int i;
            string strDate;
            strDate = DateTime.Today.ToString("yyyy-MM-dd");

            for (i = 1; i <= strDate.Length; i++)
            {
                if (IsNumeric(strDate.Substring(i - 1, 1)))
                {
                }
            }

            return strDate;
        }

        private void SelNext(int intNext)
        {
            int i;
            int intpos1, intpos2;
            intpos1 = 0;  //position of the first delimeter
            intpos2 = 0;  //position of the second delimeter

            for (i = 1; i <= txtDate.Text.Length; i++)
            {
                if (!IsNumeric(txtDate.Text.Substring(i - 1, 1)))
                {
                    if (intpos1 != 0)
                        intpos2 = i;
                    else
                        intpos1 = i;
                }
            }

            if (intpos2 != 0)
            {
                //If exists the second delimeter and the first delimeter
                switch (intNext)
                {
                    case -1:
                        if (txtDate.SelectionStart >= intpos2)
                        {
                            txtDate.SelectionStart = intpos1;
                            txtDate.SelectionLength = intpos2 - intpos1 - 1;
                        }
                        else
                        {
                            if (txtDate.SelectionStart >= intpos1)
                            {
                                txtDate.SelectionStart = 0;
                                txtDate.SelectionLength = intpos1 - 1;
                            }
                        }
                        break;
                    case 0:
                        if (txtDate.SelectionStart < intpos1)
                        {
                            txtDate.SelectionStart = 0;
                            txtDate.SelectionLength = intpos1 - 1;
                        }
                        else
                            if (txtDate.SelectionStart < intpos2)
                            {
                                txtDate.SelectionStart = intpos1;
                                txtDate.SelectionLength = intpos2 - intpos1 - 1;
                            }
                            else
                            {
                                txtDate.SelectionStart = intpos2;
                                txtDate.SelectionLength = txtDate.Text.Length - intpos2;
                            }
                        break;
                    case 1:
                        if (txtDate.SelectionStart < intpos1)
                        {
                            txtDate.SelectionStart = intpos1;
                            txtDate.SelectionLength = intpos2 - intpos1 - 1;
                        }
                        else
                        {
                            if (txtDate.SelectionStart < intpos2)
                            {
                                txtDate.SelectionStart = intpos2;
                                txtDate.SelectionLength = txtDate.Text.Length - intpos2 + 1;
                            }
                        }
                        break;
                }
            }
            else
            {
                int intSeledLen;
                intSeledLen = txtDate.SelectionLength;
                switch (intNext)
                {
                    case -1:
                        if (txtDate.SelectionStart - txtDate.SelectionLength >= 0)
                        {
                            txtDate.SelectionStart = txtDate.SelectionStart - intSeledLen;
                            txtDate.SelectionLength = intSeledLen;
                        }
                        else
                        {
                            intSeledLen = txtDate.SelectionStart;
                            txtDate.SelectionStart = 0;
                            txtDate.SelectionLength = intSeledLen;
                        }
                        break;
                    case 0:
                        break;
                    case 1:
                        if (txtDate.SelectionStart + 2 * intSeledLen > txtDate.Text.Length)
                        {
                            txtDate.SelectionStart = txtDate.SelectionStart + intSeledLen;
                            txtDate.SelectionLength = txtDate.Text.Length - txtDate.SelectionStart;
                        }
                        else
                        {
                            txtDate.SelectionStart = txtDate.SelectionStart + intSeledLen;
                            txtDate.SelectionLength = intSeledLen;
                        }
                        break;
                }
            }
        }

        private bool IsNumeric(string Value)
        {


            if (Value == null || Value == string.Empty)
            {
                return false;
            }
            else
            {
                if (Value.Substring(0, 1) == "-")
                {
                    Value = Value.Substring(1, Value.Length - 1);
                }

                foreach (char c in Value.ToCharArray())
                {
                    if (!(char.IsNumber(c) || c == '.'))
                    {
                        return false;
                    }
                }
                return true;
            }
        }

        private int ToInt32(string a_strPara, Int32 a_intDefault)
        {
            if (a_strPara == null) return a_intDefault;
            if (!IsInt(a_strPara)) return a_intDefault;
            try
            {
                return Convert.ToInt32(a_strPara);
            }
            catch
            {
                return a_intDefault;
            }
        }

        private int ToInt32(Object a_strPara, Int32 a_intDefault)
        {
            if (a_strPara == null) return a_intDefault;
            try
            {
                return Convert.ToInt32(a_strPara);
            }
            catch
            {
                return a_intDefault;
            }
        }

        private bool IsInt(string Value)
        {
            if (Value == null || Value == string.Empty)
            {
                return false;
            }
            else
            {
                foreach (char c in Value.ToCharArray())
                {
                    if (!char.IsNumber(c))
                        return false;
                }
                return true;
            }
        }
        #endregion

        private void DateTimeEx_Resize(object sender, EventArgs e)
        {
            SetSize();
        }

        private void SetSize()
        {
            switch (this.BorderStyle)
            {

                case BorderStyle.FixedSingle:
                    this.dtpDate.Left = -1;
                    this.dtpDate.Top = -1;

                    this.txtDate.Top = -1;
                    this.txtDate.Left = -1;
                    break;
                case BorderStyle.Fixed3D:
                    this.dtpDate.Left = 0;
                    this.dtpDate.Top = 0;

                    this.txtDate.Top = 0;
                    this.txtDate.Left = 0;
                    break;

                case BorderStyle.None:
                    this.dtpDate.Left = 0;
                    this.txtDate.Left = 3;

                    this.dtpDate.Top = 0;
                    this.txtDate.Top = 3;
                    break;

            }

            this.Height = this.dtpDate.Height;
            this.dtpDate.Width = this.Width;
            this.txtDate.Width = this.Width - 16;
            txtDate.SelectionStart = 0; 
        }

        private void txtDate_MouseDown(object sender, MouseEventArgs e)
        {
            base.OnMouseDown(e); 
        }

        private void dtpDate_MouseDown(object sender, MouseEventArgs e)
        {
            base.OnMouseDown(e);

            if (m_bIsColsed)
            {
                if (m_LastDate.ToString("yyyy-MM-dd") == "1900-01-01")
                    this.txtDate.Text = string.Empty;
                else
                    this.txtDate.Text = m_LastDate.ToString("yyyy-MM-dd");
            }
        }

        private void txtDate_MouseClick(object sender, MouseEventArgs e)
        {
            base.OnMouseClick(e); 
        }

        private void DateTimeEx_Enter(object sender, EventArgs e)
        {
          // txtDate.Focus(); 
        }

    }
}
